package cc.mallet.topics.gui;

import java.awt.*;
import java.awt.event.*;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.lang.reflect.*;

import javax.swing.*;
import javax.swing.filechooser.FileFilter;

import java.util.*;
import java.util.List;

/**
 * The Class TopicModelingGUI.
 */
public class TopicModelingTool {

	private static final String DEFAULT_STOP_LIST = "Mallet Default";

	/** the delimiter for csv files */
	public static final String CSV_DEL = ",";
	/** used for testing to set a input dir on startup */

	private static String absoluteClassPath = new File("").getAbsolutePath();

	private static final String ENRON_STOP_WORD_FILE = absoluteClassPath
			+ File.separator + "etc" + File.separator + "stoplists"
			+ File.separator + "en.txt";

	private static final String REFERENCE_LABEL_FILE = absoluteClassPath
			+ File.separator + "etc" + File.separator + "topicLabels.csv";

	public static String DEFAULT_INPUT_DIR = absoluteClassPath + File.separator
			+ "etc" + File.separator + "trainingSet";

	public static final String DEFAULT_OUTPUT_DIR = absoluteClassPath
			+ File.separator + "etc" + File.separator + "trainingResult";

	private static final String INFERENCER_FILE = DEFAULT_OUTPUT_DIR
			+ File.separator + "inferencer.mallet";
	private static final String EVALUATOR_FILE = DEFAULT_OUTPUT_DIR
			+ File.separator + "evaluator.mallet";

	public static String DEFAULT_TEST_INPUT_DIR = absoluteClassPath
			+ File.separator + "etc" + File.separator + "testingSet";

	public static String DEFAULT_TEST_OUTPUT_DIR = absoluteClassPath
			+ File.separator + "etc" + File.separator + "testingResult";

	private static final String newline = "\n";

	private JFrame appFrame;
	private JFrame frame2;

	private JPanel mainPanel;

	private JDialog helpPane1;
	private JDialog helpPane2;

	private JButton openInputButton;
	private JButton openTestButton;
	private JButton trainButton;
	private JButton labelButton;
	private JButton stopChooseButton;
	private JButton openOutputButton;
	private JButton inferButton;
	private JButton clearButton;
	private JButton evaluateButton;

	private JTextArea log;

	private JFileChooser inputFileChooser;
	private JFileChooser outputFileChooser;
	private JFileChooser stopwordFileChooser = new JFileChooser();

	private JTextField inputDirTfield = new JTextField();
	private JTextField outputDirTfield = new JTextField();
	private JTextField inputTestDirTfield = new JTextField();
	private JTextField numTopicsField;
	private JTextField evaluatorFileField;
	private JTextField inferencerFileField;
	private JTextField stopFileField = new JTextField(ENRON_STOP_WORD_FILE);

	private LinkedHashMap<String, String[]> checkBoxOptionMap = new LinkedHashMap<String, String[]>();
	private LinkedHashMap<String, String[]> fieldOptionMap = new LinkedHashMap<String, String[]>();
	private HashMap<String, String> otherOptionsMap = new HashMap<String, String>();

	private JCheckBox stopBox;

	private ArrayList<JCheckBox> advCheckBoxList = new ArrayList<JCheckBox>();
	private ArrayList<JTextField> advFieldList = new ArrayList<JTextField>();

	private Boolean frameBusy = false;

	/**
	 * Update text area.
	 * 
	 * @param text
	 *            the text
	 */
	private void updateTextArea(final String text) {
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				log.append(text);
				log.setCaretPosition(log.getDocument().getLength());
			}
		});
	}

	/**
	 * Redirect system streams.
	 */
	private void redirectSystemStreams() {
		OutputStream out = new OutputStream() {
			@Override
			public void write(int b) throws IOException {
				updateTextArea(String.valueOf((char) b));
			}

			@Override
			public void write(byte[] b, int off, int len) throws IOException {
				updateTextArea(new String(b, off, len));
			}

			@Override
			public void write(byte[] b) throws IOException {
				write(b, 0, b.length);
			}
		};

		System.setOut(new PrintStream(out, true));
		System.setErr(new PrintStream(out, true));
	}

	/**
	 * Creates the help panel in the Basic window.
	 */
	public void createHelp1() {

		helpPane1 = new JDialog();
		JPanel p1 = new JPanel();
		String text = "<html><b>Input - </b>Select a directory containing text files, or a single text file where each line is a"
				+ " data instance.<br><br> <b>Output Directory -</b> All generated output is written to this folder. Current directory by default.<br><br>"
				+ "<b>Number of Topics -</b> The number of topics to fit.<br></html>";

		JLabel b = new JLabel(text, SwingConstants.LEFT) {
			public Dimension getPreferredSize() {
				return new Dimension(400, 150);
			}

			public Dimension getMinimumSize() {
				return new Dimension(400, 150);
			}

			public Dimension getMaximumSize() {
				return new Dimension(400, 150);
			}
		};
		b.setVerticalAlignment(SwingConstants.CENTER);

		p1.add(b);
		p1.setBorder(BorderFactory.createCompoundBorder(
				BorderFactory.createTitledBorder("Help"),
				BorderFactory.createEmptyBorder(10, 10, 10, 10)));
		helpPane1.setContentPane(p1);
		helpPane1.setTitle("Basic Options");
		// helpPane1.setModal(true);
		helpPane1.setResizable(false);
		helpPane1.pack();
		helpPane1.setDefaultCloseOperation(JDialog.HIDE_ON_CLOSE);

	}

	/**
	 * Show help in Basic panel.
	 */
	public void showHelp1() {

		helpPane1.setVisible(true);

	}

	/**
	 * Show help in Advanced panel.
	 */
	public void showHelp2() {

		helpPane2.setVisible(true);

	}

	/**
	 * Creates the help panel in the Advanced window.
	 */
	public void createHelp2() {
		helpPane2 = new JDialog();
		JPanel p1 = new JPanel();

		String text = "<html><b>Remove stopwords - </b>If checked, remove a list of \"stop words\" from the text.<br><br>"
				+ " <b>Stopword file - </b>Read \"stop words\" from a file, one per line. Default is Mallet's list of standard English stopwords.<br><br>"
				+ "<b>Case sensitive - </b>If checked, do not force all strings to lowercase.<br><br><b>No. of iterations - </b> The number of iterations of Gibbs sampling to run. Default is 1000.<br><br>"
				+ "<b>No. of topic words printed - </b>The number of most probable words to print for each topic after model estimation.<br><br>"
				+ "<b>Topic proportion threshold - </b>Do not print topics with proportions less than this threshold value.</b></html>";

		JLabel b = new JLabel(text, SwingConstants.LEFT) {
			public Dimension getPreferredSize() {
				return new Dimension(400, 300);
			}

			public Dimension getMinimumSize() {
				return new Dimension(400, 300);
			}

			public Dimension getMaximumSize() {
				return new Dimension(400, 300);
			}
		};
		b.setVerticalAlignment(SwingConstants.CENTER);

		p1.add(b);
		p1.setBorder(BorderFactory.createCompoundBorder(
				BorderFactory.createTitledBorder("Help"),
				BorderFactory.createEmptyBorder(10, 10, 10, 10)));
		helpPane2.setContentPane(p1);
		helpPane2.setTitle("Advanced Options");
		// helpPane2.setModal(true);
		helpPane2.setResizable(false);
		helpPane2.pack();
		helpPane2.setDefaultCloseOperation(JDialog.HIDE_ON_CLOSE);

	}

	/**
	 * Listener interface for help request in the Basic window.
	 */

	public class BasicHelpListener extends MouseAdapter {
		@Override
		public void mouseClicked(MouseEvent e) {
			showHelp1();

		}
	}

	/**
	 * Listener interface for help requests in the Advanced window.
	 */

	public class AdvHelpListener extends MouseAdapter {

		@Override
		public void mouseClicked(MouseEvent e) {
			showHelp2();

		}
	}

	public class OpenInputButtonListener implements ActionListener {

		public void actionPerformed(ActionEvent e) {
			int returnVal = inputFileChooser.showOpenDialog(mainPanel);

			if (returnVal == JFileChooser.APPROVE_OPTION) {
				File file = inputFileChooser.getSelectedFile();
				String inputDir = file.getPath();
				if (file.isDirectory())
					log.append("Chose Input Directory: " + inputDir + newline);
				else
					log.append("Chose Input File: " + inputDir + newline);

				inputDirTfield.setText(inputDir);

			} else {
				log.append("Open command cancelled by user." + newline);
			}

			log.setCaretPosition(log.getDocument().getLength());
		}
	}

	public class OpenOutputButtonListener implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			int returnVal = outputFileChooser.showOpenDialog(mainPanel);

			if (returnVal == JFileChooser.APPROVE_OPTION) {
				File file = outputFileChooser.getSelectedFile();
				String inputDir = file.getPath();
				log.append("Chose Output Directory: " + inputDir + newline);
				outputDirTfield.setText(inputDir);

			} else {
				log.append("Open command cancelled by user." + newline);
			}
			log.setCaretPosition(log.getDocument().getLength());
		}
	}

	public class OpenTestButtonListener implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			inputFileChooser.setCurrentDirectory(new File(
					DEFAULT_TEST_INPUT_DIR));
			int returnVal = inputFileChooser.showOpenDialog(mainPanel);

			if (returnVal == JFileChooser.APPROVE_OPTION) {
				File file = inputFileChooser.getSelectedFile();
				String inputTestDir = file.getPath();
				if (file.isDirectory())
					log.append("Chose Input Test Directory: " + inputTestDir
							+ newline);
				else
					log.append("Chose Input Test File: " + inputTestDir
							+ newline);
				inputTestDirTfield.setText(inputTestDir);
			} else {
				log.append("Open command cancelled by user." + newline);
			}
			log.setCaretPosition(log.getDocument().getLength());
		}
	}

	/**
	 * The listener interface for receiving stopBox events.
	 * 
	 */
	public class StopBoxListener implements ActionListener {

		/**
		 * When the stopwords checkbox is checked, enable the stopword file
		 * button.
		 */
		public void actionPerformed(ActionEvent e) {
			stopChooseButton.setEnabled(stopBox.isSelected());
		}
	}

	/**
	 * The listener interface for receiving stopChooser events. Shows the file
	 * chooser dialog for setting a stopword list file.
	 */
	public class StopChooserListener implements ActionListener {

		/*
		 * (non-Javadoc)
		 * 
		 * @see
		 * java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent
		 * )
		 */
		public void actionPerformed(ActionEvent e) {
			int returnVal = stopwordFileChooser.showOpenDialog(mainPanel);

			if (returnVal == JFileChooser.APPROVE_OPTION) {
				File file = stopwordFileChooser.getSelectedFile();
				String inputDir = file.getPath();
				log.append("Chose Stopword File: " + inputDir + newline);
				stopFileField.setText(inputDir);
			} else {
				log.append("Open command cancelled by user." + newline);
			}
			log.setCaretPosition(log.getDocument().getLength());
		}
	}

	/**
	 * The listener interface for receiving frameFocus events. To show a busy
	 * hour glass icon when the Basic window is in focus.
	 */
	public class FrameFocusListener implements FocusListener {

		/*
		 * (non-Javadoc)
		 * 
		 * @see
		 * java.awt.event.FocusListener#focusGained(java.awt.event.FocusEvent)
		 */
		@Override
		public void focusGained(FocusEvent arg0) {
			if (frameBusy) {
				Cursor hourglassCursor = new Cursor(Cursor.WAIT_CURSOR);
				appFrame.setCursor(hourglassCursor);
			}

		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see
		 * java.awt.event.FocusListener#focusLost(java.awt.event.FocusEvent)
		 */
		@Override
		public void focusLost(FocusEvent arg0) {

		}

	}

	/**
	 * The listener interface for receiving advancedButton events. Clicking
	 * should bring up the Advanced panel.
	 * 
	 */
	public class AdvancedButtonListener implements ActionListener {

		/*
		 * (non-Javadoc)
		 * 
		 * @see
		 * java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent
		 * )
		 */
		public void actionPerformed(ActionEvent e) {
			frame2.setVisible(true);

		}
	}

	/**
	 * The listener interface for receiving inferButton events.
	 * 
	 */
	public class InferButtonListener implements ActionListener {
		private Thread t;

		@Override
		public void actionPerformed(ActionEvent arg0) {
			// TODO Auto-generated method stub
			t = new Thread() {
				public void run() {
					inferTopics();
				}
			};

			t.start();
		}

		private void inferTopics() {
			long start = System.currentTimeMillis();

			String testInputTop = inputTestDirTfield.getText();
			String testInstanceFile = testInputTop + File.separator
					+ "test-instance.mallet";

			log.append("Importing and Infering...this may take a few minutes depending on collection size.");

			// 10-fold cross validation
			for (int i = 0; i < 10; i++) {
				String testInput = testInputTop + File.separator + i;
				String testOutput = DEFAULT_TEST_OUTPUT_DIR + File.separator
						+ "test-output" + i + ".txt";

				log.append("Importing from: " + testInput + "." + newline);
				log.setCaretPosition(log.getDocument().getLength());

				try {

					String[] advArgs = importData(testInstanceFile, testInput);

					Class clazz = Class
							.forName("cc.mallet.topics.tui.InferTopics");

					String[] inferArgs = { "--input", testInstanceFile,
							"--inferencer", INFERENCER_FILE,
							"--output-doc-topics", testOutput,
							"--doc-topics-threshold", "0.02",
							"--num-iterations", "2500" };

					Class[] argTypes = new Class[] { inferArgs.getClass() }; // array
					// is
					// Object!
					Method m = clazz.getMethod("main", argTypes);
					Object[] passedArgs = new Object[] { inferArgs };
					System.out.println(Arrays.toString(inferArgs));
					log.append("Infering..." + newline);
					m.invoke(null, passedArgs);

				} catch (Throwable e1) {
					e1.printStackTrace();
				}

			}
			System.out.println("FINISH!");

			Cursor normalCursor = new Cursor(Cursor.DEFAULT_CURSOR);
			appFrame.setCursor(normalCursor);
			frameBusy = false;

			// TODO Open and parse TopicsInDocs.csv
			// top topic at element 2, 4 and 6
			calculateTestingResults();
			long elapsedTimeMillis = System.currentTimeMillis() - start;

			// Get elapsed time in seconds
			float elapsedTimeSec = elapsedTimeMillis / 1000F;
			System.out.println("Total 10-fold cross validation time: "
					+ elapsedTimeSec + " seconds");

		}

		private void calculateTestingResults() {
			System.out.println("Performing 10-fold cross validation...");

			// TODO Auto-generated method stub
			Map<String, List<String>> referenceTopics = new HashMap<String, List<String>>();

			String outputDir = outputDirTfield.getText();
			String csvPathname = outputDir + File.separator + "output_csv"
					+ File.separator + "TopicsInDocs.csv";

			File csvFile = new File(csvPathname);

			try {
				String line = "";
				String[] st = null;

				// Read the raw topic words from cvs file
				BufferedReader in = new BufferedReader(new FileReader(csvFile));
				in.readLine(); // ignore header
				while ((line = in.readLine()) != null) {
					if (line.isEmpty()) {
						continue;
					}
					st = line.split(CSV_DEL);

					String fileName = st[1];

					System.out.println("Processing " + fileName);

					fileName = fileName.substring(
							fileName.lastIndexOf("/") + 1, fileName.length());
					List<String> topTopics = new ArrayList<String>();

					referenceTopics.put(fileName, topTopics);

					if (st[2] != null && !st[2].isEmpty()) {
						topTopics.add(st[2]);
					}
					if (st.length > 4) {
						if (st[4] != null && !st[4].isEmpty()) {
							topTopics.add(st[4]);
						}
					}

					if (st.length > 6) {
						if (st[6] != null && !st[6].isEmpty()) {
							topTopics.add(st[6]);
						}
					}

				}
				in.close();

				int total = 0;
				int correct = 0;

				for (int i = 0; i < 10; i++) {
					String testResultFile = DEFAULT_TEST_OUTPUT_DIR
							+ File.separator + "test-output" + i + ".txt";
					BufferedReader testIn = new BufferedReader(new FileReader(
							testResultFile));
					testIn.readLine(); // ignore header

					while ((line = testIn.readLine()) != null) {
						if (line.isEmpty()) {
							continue;
						}
						total++;
						st = line.split(" ");
						String testFileName = st[1];
						testFileName = testFileName.substring(
								testFileName.lastIndexOf(File.separator) + 1,
								testFileName.length());

						String topTestTopic = st[2];

						List<String> list = referenceTopics.get(testFileName);
						for (String refTopic : list) {
							if (refTopic.equals(topTestTopic)) {
								correct++;
								break;
							}
						}
					}

					testIn.close();
				}
				double accuracy = ((double) correct / (double) total) * 100;

				System.out
						.println("Finish performing 10-fold cross validation");
				System.out.println("Correct: " + correct);
				System.out.println("Total: " + total);
				System.out.println("Accuracy: "
						+ String.format("%2.1f", accuracy) + "%");
			} catch (Exception e) {
				System.err.println(e);
			}

		}

	}

	public class EvaluateButtonListener implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent arg0) {
			// TODO Auto-generated method stub
			String testDir = inputTestDirTfield.getText();
			File testFile = new File(testDir);
			log.append("Importing and Evaluating...this may take a few minutes depending on collection size.");

			log.append("Importing from: " + testDir + "." + newline);
			log.setCaretPosition(log.getDocument().getLength());

		}

	}

	/**
	 * The listener interface for receiving trainButton events.
	 * 
	 */
	public class TrainButtonListener implements ActionListener {

		/*
		 * Start a new thread that will execute the runMallet method
		 */

		Thread t;

		public void actionPerformed(ActionEvent e) {
			// Get current time
			t = new Thread() {
				public void run() {
					runMallet();
				}
			};

			t.start();

		}

		/**
		 * Method that assembles all the options given by the user through the
		 * GUI and runs Mallet's importing and topic modeling methods.
		 */
		public void runMallet() {
			long start = System.currentTimeMillis();
			if (inputDirTfield.getText().equals("")) {
				JOptionPane.showMessageDialog(mainPanel,
						"Please select an input file or directory",
						"Invalid input", JOptionPane.ERROR_MESSAGE);
			} else {

				clearButton.setEnabled(false);
				trainButton.setEnabled(false);

				String outputDir = outputDirTfield.getText();
				String collectionPath = new File(outputDir,
						"topic-input.mallet").getPath(); // FIXME
				String inputDir = inputDirTfield.getText();

				try {

					String[] advArgs = importData(collectionPath, inputDir);

					train(outputDir, collectionPath, advArgs);

					enableTestPanel(true);
				} catch (Throwable e1) {
					e1.printStackTrace();
				}
				log.setCaretPosition(log.getDocument().getLength());
				clearButton.setEnabled(true);

				long elapsedTimeMillis = System.currentTimeMillis() - start;

				// Get elapsed time in seconds
				float elapsedTimeSec = elapsedTimeMillis / 1000F;
				System.out.println("Total time training: " + elapsedTimeSec
						+ " seconds");

				Cursor normalCursor = new Cursor(Cursor.DEFAULT_CURSOR);

				trainButton.setEnabled(true);
				inferButton.setEnabled(true);

				appFrame.setCursor(normalCursor);
				frameBusy = false;
			}
		}

	}

	public class LabelButtonListener implements ActionListener {

		private static final String SPACE_DEL = " ";

		@Override
		public void actionPerformed(ActionEvent e) {
			// TODO Auto-generated method stub
			System.out.println("Performing topic labeling...");
			labelTopic();
		}

		private void labelTopic() {
			// TODO Auto-generated method stub
			String outputDir = outputDirTfield.getText();
			String csvPathname = outputDir + File.separator + "output_csv"
					+ File.separator + "Topics_Words.csv";

			File csvFile = new File(csvPathname);
			File refLabelFile = new File(REFERENCE_LABEL_FILE);
			File labeledTopicFile = new File(outputDir, "labeled_topics.csv");

			List<String> topics = new ArrayList<String>();
			Map<String, String> topicLableMap = new HashMap<String, String>();

			try {
				String line = "";
				String[] st = null;

				// Read the raw topic words from cvs file
				BufferedReader in = new BufferedReader(new FileReader(csvFile));
				in.readLine(); // ignore header
				while ((line = in.readLine()) != null) {
					if (line.isEmpty()) {
						continue;
					}
					st = line.split(CSV_DEL);
					topics.add(st[1]);
				}
				in.close();

				// Read the topic label reference file
				BufferedReader refIn = new BufferedReader(new FileReader(
						refLabelFile));
				refIn.readLine(); // ignore header
				while ((line = refIn.readLine()) != null) {
					if (line.isEmpty()) {
						continue;
					}
					st = line.split(CSV_DEL);
					topicLableMap.put(st[0], st[1]);
				}
				refIn.close();

				BufferedWriter out = new BufferedWriter(new FileWriter(
						labeledTopicFile));
				// Write header.
				out.write("topic, label" + "\n");

				for (String topic : topics) {
					boolean matched = false;
					String[] t = topic.split(SPACE_DEL);
					Set<String> topicSet = new HashSet<String>();
					for (int i = 0; i < t.length; i++) {
						topicSet.add(t[i]);
					}
					for (String topicKey : topicLableMap.keySet()) {
						String[] r = topicKey.split(SPACE_DEL);
						Set<String> refSet = new HashSet<String>();
						for (int i = 0; i < r.length; i++) {
							refSet.add(r[i]);
						}
						if (topicSet.containsAll(refSet)) {
							System.out.println("*** Label found *** [" + topic
									+ "] ==> " + topicLableMap.get(topicKey));
							out.write(topic + CSV_DEL
									+ topicLableMap.get(topicKey) + "\n");
							matched = true;
						}
					}
					if (!matched) {
						System.out
								.println("No label found for [" + topic + "]");
						out.write(topic + CSV_DEL + "none" + "\n");
					}
				}

				out.flush();
				out.close();

				System.out.println("Finish labeling");
			} catch (Exception e) {
				System.err.println(e);
			}

		}

	}

	/**
	 * Clear console area
	 */
	public class ClearButtonListener implements ActionListener {

		/*
		 * (non-Javadoc)
		 * 
		 * @see
		 * java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent
		 * )
		 */
		public void actionPerformed(ActionEvent e) {
			log.setText("");
		}
	}

	/**
	 * The listener interface for receiving resetButton events.
	 */
	public class ResetButtonListener implements ActionListener {

		/*
		 * (non-Javadoc)
		 * 
		 * @see
		 * java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent
		 * )
		 */
		public void actionPerformed(ActionEvent e) {
			resetAdvControls();
		}
	}

	/**
	 * Returns an ImageIcon, or null if the path was invalid.
	 * 
	 * @param path
	 *            the path
	 * @return the image icon
	 */
	protected static ImageIcon createImageIcon(String path) {
		java.net.URL imgURL = TopicModelingTool.class.getResource(path);
		if (imgURL != null) {
			return new ImageIcon(imgURL);
		} else {
			System.err.println("Couldn't find file: " + path);
			return null;
		}
	}

	/**
	 * 
	 * Set corresponding special string, default value, description and
	 * associated command for the options
	 */
	public void setDefaultOptions() {

		checkBoxOptionMap.put("--enron-dataset", new String[] {
				"Skip header", "TRUE", "import" });
		// checkBoxOptionMap.put("--skip-header", new String[] { "Skip header",
		// "TRUE", "import" });
		checkBoxOptionMap.put("--skip-html", new String[] { "Skip HTML tags",
				"TRUE", "import" });
		checkBoxOptionMap.put("--keep-sequence-bigrams", new String[] {
				"Remove non-word tokens", "TRUE", "import" });
//		checkBoxOptionMap.put("--stemming", new String[] { "Stemming", "FALSE",
//				"import" });
		checkBoxOptionMap.put("--remove-stopwords", new String[] {
				"Remove stopwords", "TRUE", "import" });
		checkBoxOptionMap.put("--preserve-case", new String[] {
				"Case sensitive", "FALSE", "import" });

//		fieldOptionMap
//				.put("--alpha", new String[] { "alpha", "50.0", "train" });
//		fieldOptionMap.put("--beta", new String[] { "beta", "0.01", "train" });
//		fieldOptionMap
//				.put("--delta", new String[] { "delta", "0.03", "train" });
		fieldOptionMap.put("--num-iterations", new String[] {
				"No. of iterations", "2500", "train" });
		fieldOptionMap.put("--num-top-words", new String[] {
				"No. of topic words printed", "5", "train" });
		fieldOptionMap.put("--doc-topics-threshold", new String[] {
				"Topic proportion threshold", "0.02", "train" });
		fieldOptionMap.put("--num-threads", new String[] { "No. of threads",
				"1", "train" });
		fieldOptionMap.put("--optimize-interval", new String[] {
				"Hyperparameter Optimization Interval", "50", "train" });

		otherOptionsMap.put("--stoplist-file", new String("import"));
		otherOptionsMap.put("--evaluator-filename", new String("train"));
		otherOptionsMap.put("--inferencer-filename", new String("train"));

	}

	/**
	 * Initializes the advanced controls.
	 */
	public void initAdvancedControls() {
		for (String k : fieldOptionMap.keySet()) {
			JTextField tempField = new JTextField(fieldOptionMap.get(k)[1], 4);
			advFieldList.add(tempField);
		}

		// Excusively select --enron-dataset or --skip-header checkbox but not
		// both.
		// ButtonGroup buttonGroup = new ButtonGroup();

		for (String k : checkBoxOptionMap.keySet()) {
			JCheckBox tempCheckBox = new JCheckBox(checkBoxOptionMap.get(k)[0]);
			if (checkBoxOptionMap.get(k)[1].equals("TRUE")) {
				tempCheckBox.setSelected(true);
			}
			advCheckBoxList.add(tempCheckBox);
			if (k.equals("--remove-stopwords")) {
				tempCheckBox.addActionListener(new StopBoxListener());
				stopBox = tempCheckBox;
			}
			// else if (k.equals("--enron-dataset")) {
			// buttonGroup.add(tempCheckBox);
			// } else if (k.equals("--skip-header")) {
			// buttonGroup.add(tempCheckBox);
			// }
		}
	}

	/**
	 * Reset advanced controls to default values.
	 */
	public void resetAdvControls() {

		Iterator<JTextField> itr1 = advFieldList.iterator();
		for (String[] k : fieldOptionMap.values()) {
			itr1.next().setText(k[1]);
		}

		Iterator<JCheckBox> itr2 = advCheckBoxList.iterator();
		for (String[] k : checkBoxOptionMap.values()) {
			JCheckBox jc = itr2.next();
			if (k[1].equals("TRUE")) {
				jc.setSelected(true);
			} else {
				jc.setSelected(false);
			}
		}

		stopFileField.setText(DEFAULT_STOP_LIST);
	}

	/**
	 * Gets the adv args.
	 * 
	 * @return the adv args
	 */
	public String[] getAdvArgs() {

		String[] advArgs = new String[(checkBoxOptionMap.size()
				+ fieldOptionMap.size() + 3) * 2]; // +3 for stopword list,
													// evaluator and inferencer
		int index = 0;

		Iterator<JCheckBox> itr1 = advCheckBoxList.iterator();
		for (String k : checkBoxOptionMap.keySet()) {
			advArgs[index] = k;
			boolean b = itr1.next().isSelected();
			advArgs[index + 1] = new Boolean(b).toString();
			index = index + 2;
		}

		Iterator<JTextField> itr2 = advFieldList.iterator();
		for (String k : fieldOptionMap.keySet()) {
			advArgs[index] = k;
			if (k.equals("--num-top-words")) {
				int topwords = Integer.parseInt(itr2.next().getText()) + 1; // spl
																			// case
																			// -
																			// potential
																			// mallet
																			// bug
				advArgs[index + 1] = new Integer(topwords).toString();
			} else {
				advArgs[index + 1] = itr2.next().getText();
			}
			index = index + 2;
		}

		//
		if (stopFileField.getText().equals(DEFAULT_STOP_LIST)) {
			advArgs[index] = "";
			advArgs[index + 1] = "";
		} else {
			advArgs[index] = "--stoplist-file";
			advArgs[index + 1] = stopFileField.getText();
		}

		if (hasText(evaluatorFileField)) {
			index = index + 2;
			advArgs[index] = "--evaluator-filename";
			advArgs[index + 1] = evaluatorFileField.getText();
		}

		if (hasText(inferencerFileField)) {
			index = index + 2;
			advArgs[index] = "--inferencer-filename";
			advArgs[index + 1] = inferencerFileField.getText();
		}

		return advArgs;

	}

	private boolean hasText(JTextField field) {
		return field != null && !(field.getText().isEmpty());
	}

	/**
	 * Go.
	 */
	public void go() {
		redirectSystemStreams();

		setDefaultOptions();
		initAdvancedControls();

		clearButton = new JButton("Clear Console");
		clearButton.addActionListener(new ClearButtonListener());

		JTabbedPane tabbedPane = new JTabbedPane();
		tabbedPane.addTab("PreProcess", createPreProcessPanel());
		tabbedPane.addTab("Learn", createLearnPanel());
		tabbedPane.addTab("PostProcess", createPostProcessPanel());
		tabbedPane.addTab("Test", createTestPanel());

		log = new JTextArea(20, 20);
		log.setMargin(new Insets(5, 5, 5, 5));
		log.setEditable(false);
		JScrollPane logScrollPane = new JScrollPane(log);
		logScrollPane.setBorder(BorderFactory.createTitledBorder("Console"));

		mainPanel = new JPanel(new BorderLayout());
		mainPanel.add(tabbedPane, BorderLayout.NORTH);
		mainPanel.add(logScrollPane, BorderLayout.CENTER);
		mainPanel.add(clearButton, BorderLayout.SOUTH);
		mainPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
		mainPanel.setOpaque(true);// content panes must be opaque

		appFrame = new JFrame("Email Topic Modeling Tool");
		appFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		appFrame.addFocusListener(new FrameFocusListener());
		appFrame.setContentPane(mainPanel);
		appFrame.setLocation(500, 100);
		appFrame.pack();
		appFrame.setVisible(true);

		createHelp1();
		createHelp2();
	}

	private JPanel createPostProcessPanel() {
		JPanel inputPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));

		labelButton = new JButton("Label Topic");

		labelButton.setToolTipText("Label LDA topic result from learing");
		labelButton.addActionListener(new LabelButtonListener());

		JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
		buttonPanel.add(labelButton);

		JPanel postProcessPanel = new JPanel(new BorderLayout());
		postProcessPanel.add(inputPanel, BorderLayout.CENTER);
		postProcessPanel.add(buttonPanel, BorderLayout.SOUTH);
		return postProcessPanel;
	}

	private void enableTestPanel(boolean enabled) {
		inputTestDirTfield.setEditable(enabled);
		openTestButton.setEnabled(enabled);
		inferButton.setEnabled(enabled);
		evaluateButton.setEnabled(enabled);
	}

	private JPanel createLearnPanel() {
		trainButton = new JButton("<html><b>Learn Topics</b><html>",
				createImageIcon("/images/gears.png"));
		trainButton.addActionListener(new TrainButtonListener());

		JPanel learnPanel = new JPanel(new GridBagLayout());
		GridBagConstraints c = new GridBagConstraints(0, 0, 1, 1, 1, 1,
				GridBagConstraints.FIRST_LINE_START,
				GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0);

		c.gridwidth = 3;
		numTopicsField = new JTextField("5", 2);
		JPanel topicPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
		topicPanel.add(new Label("Number of Topics"));
		topicPanel.add(numTopicsField);
		learnPanel.add(topicPanel, c);

		c.gridx = 0;
		c.gridy = 1;
		evaluatorFileField = new JTextField(EVALUATOR_FILE);
		JPanel evaluatorPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
		evaluatorPanel.add(new Label("Evaluator File"));
		evaluatorPanel.add(evaluatorFileField);
		learnPanel.add(evaluatorPanel, c);

		c.gridx = 0;
		c.gridy = 2;
		inferencerFileField = new JTextField(INFERENCER_FILE);
		JPanel inferencerPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
		inferencerPanel.add(new Label("Inferencer File"));
		inferencerPanel.add(inferencerFileField);
		learnPanel.add(inferencerPanel, c);

		c.gridx = 0;
		c.gridy = 3;
		c.gridwidth = 1;
		Collection<String[]> fieldOptions = fieldOptionMap.values();
		Iterator<String[]> itr = fieldOptions.iterator();
		int count = 1;
		for (int i = 0; i < advFieldList.size(); i++) {
			JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT));
			panel.add(new Label(itr.next()[0]));
			panel.add(advFieldList.get(i));
			learnPanel.add(panel, c);
			c.gridx++;

			if (count % 3 == 0) {
				c.gridx = 0;
				c.gridy++;
			}
			count++;
		}

		c.gridx = 0;
		c.gridy++;
		c.gridwidth = GridBagConstraints.REMAINDER;
		c.gridheight = 1;
		c.anchor = GridBagConstraints.CENTER;
		c.fill = GridBagConstraints.HORIZONTAL;
		learnPanel.add(trainButton, c);

		return learnPanel;
	}

	private JPanel createPreProcessPanel() {
		inputDirTfield.setColumns(20);
		inputDirTfield.setText(DEFAULT_INPUT_DIR);

		inputFileChooser = new JFileChooser();
		inputFileChooser
				.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
		inputFileChooser.setCurrentDirectory(new File(DEFAULT_INPUT_DIR));

		openInputButton = new JButton("Input File or Dir...",
				createImageIcon("/images/Open16.gif"));
		openInputButton.addActionListener(new OpenInputButtonListener());

		openOutputButton = new JButton("Output Dir...",
				createImageIcon("/images/Open16.gif"));
		openOutputButton.addActionListener(new OpenOutputButtonListener());
		outputFileChooser = new JFileChooser();
		outputFileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
		outputFileChooser.setCurrentDirectory(new File(DEFAULT_OUTPUT_DIR));
		outputDirTfield.setText(DEFAULT_OUTPUT_DIR);

		stopwordFileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
		stopwordFileChooser.setCurrentDirectory(new File(ENRON_STOP_WORD_FILE));
		FileFilter filter = null;
		stopwordFileChooser.setFileFilter(filter);
		stopChooseButton = new JButton("Stopword File...",
				createImageIcon("/images/Open16.gif"));
		stopChooseButton.addActionListener(new StopChooserListener());

		JPanel preProcessPanel = new JPanel(new GridBagLayout());
		GridBagConstraints c = new GridBagConstraints(0, 0, 2, 1, 1, 1,
				GridBagConstraints.FIRST_LINE_START,
				GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0);
		preProcessPanel.add(inputDirTfield, c);

		c.gridx = 2;
		c.gridy = 0;
		c.gridwidth = 1;
		c.gridheight = 1;
		c.anchor = GridBagConstraints.FIRST_LINE_START;
		c.fill = GridBagConstraints.NONE;
		preProcessPanel.add(openInputButton, c);

		c.gridx = 0;
		c.gridy = 1;
		c.gridwidth = 2;
		c.gridheight = 1;
		c.anchor = GridBagConstraints.FIRST_LINE_START;
		c.fill = GridBagConstraints.HORIZONTAL;
		preProcessPanel.add(outputDirTfield, c);

		c.gridx = 2;
		c.gridy = 1;
		c.gridwidth = 1;
		c.gridheight = 1;
		c.anchor = GridBagConstraints.FIRST_LINE_START;
		c.fill = GridBagConstraints.NONE;
		preProcessPanel.add(openOutputButton, c);

		c.gridx = 0;
		c.gridy = 2;
		c.gridwidth = 2;
		c.gridheight = 1;
		c.anchor = GridBagConstraints.FIRST_LINE_START;
		c.fill = GridBagConstraints.HORIZONTAL;
		preProcessPanel.add(stopFileField, c);

		c.gridx = 2;
		c.gridy = 2;
		c.gridwidth = 1;
		c.gridheight = 1;
		c.anchor = GridBagConstraints.FIRST_LINE_START;
		c.fill = GridBagConstraints.NONE;
		preProcessPanel.add(stopChooseButton, c);

		c.gridx = 0;
		c.gridy = 3;
		c.gridwidth = 1;
		c.gridheight = 1;
		c.anchor = GridBagConstraints.FIRST_LINE_START;
		c.fill = GridBagConstraints.NONE;

		int count = 1;
		for (int i = 0; i < advCheckBoxList.size(); i++) {
			preProcessPanel.add(advCheckBoxList.get(i), c);
			c.gridx++;

			if (count % 3 == 0) {
				c.gridx = 0;
				c.gridy++;
			}

			count++;
		}

		return preProcessPanel;
	}

	private JPanel createTestPanel() {
		inputTestDirTfield.setText(DEFAULT_TEST_INPUT_DIR);

		openTestButton = new JButton("Select Test File or Dir",
				createImageIcon("/images/Open16.gif"));
		openTestButton.addActionListener(new OpenTestButtonListener());

		JPanel inputPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
		inputPanel.add(inputTestDirTfield);
		inputPanel.add(openTestButton);

		inferButton = new JButton("Infer");
		inferButton
				.setToolTipText("Infer topics for new documents using the trained model");
		inferButton.addActionListener(new InferButtonListener());

		evaluateButton = new JButton("Evaluate");
		evaluateButton
				.setToolTipText("Estimate the probability of new documents under the trained model");
		evaluateButton.addActionListener(new EvaluateButtonListener());

		JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
		buttonPanel.add(inferButton);
		// TODO Add when evaluator implemented
		// buttonPanel.add(evaluateButton);

		enableTestPanel(false);

		JPanel testPanel = new JPanel(new BorderLayout());
		testPanel.add(inputPanel, BorderLayout.CENTER);
		testPanel.add(buttonPanel, BorderLayout.SOUTH);
		return testPanel;
	}

	private String[] importData(String instanceFile, String inputDir)
			throws ClassNotFoundException, NoSuchMethodException,
			IllegalAccessException, InvocationTargetException {
		File inputFile = new File(inputDir);
		log.append("Importing and Training...this may take a few minutes depending on collection size.");

		log.append("Importing from: " + inputDir + "." + newline);
		log.setCaretPosition(log.getDocument().getLength());

		delelteFiles(inputDir, ".DS_store");
		delelteFiles(inputDir, ".DS_Store");

		Class clazz;

		// Input to vectors.
		if (inputFile.isDirectory()) {
			clazz = Class.forName("cc.mallet.topics.gui.EnronEmail2Vectors");
		} else {
			clazz = Class.forName("cc.mallet.classify.tui.Csv2Vectors");
		}

		String[] advArgs = getAdvArgs();
		ArrayList<String> importParameters = new ArrayList<String>();
		for (int i = 0; i < advArgs.length; i = i + 2) {
			if (fieldOptionMap.containsKey(advArgs[i])) {
				if (fieldOptionMap.get(advArgs[i])[2].equals("import")) {
					importParameters.add(advArgs[i]);
					importParameters.add(advArgs[i + 1]);
				}
			} else if (checkBoxOptionMap.containsKey(advArgs[i])) {
				if (checkBoxOptionMap.get(advArgs[i])[2].equals("import")) {
					importParameters.add(advArgs[i]);
					importParameters.add(advArgs[i + 1]);
				}
			} else if (otherOptionsMap.containsKey(advArgs[i])) {
				if (otherOptionsMap.get(advArgs[i]).equals("import")) {
					importParameters.add(advArgs[i]);
					importParameters.add(advArgs[i + 1]);
				}
			}
		}

		String[] temp1 = { "--input", inputDir, "--output", instanceFile };
		Collections.addAll(importParameters, temp1);

		String[] fullImportArgs = importParameters
				.toArray(new String[importParameters.size()]);
		// Now invoke the method.
		Class[] argTypes = { fullImportArgs.getClass(), }; // array
															// is
															// Object!
		Method m = clazz.getMethod("main", argTypes);
		Object[] passedArgs = { fullImportArgs };
		System.out.println(Arrays.toString(fullImportArgs));

		Cursor hourglassCursor = new Cursor(Cursor.WAIT_CURSOR);
		appFrame.setCursor(hourglassCursor);
		frameBusy = true;

		log.append("Importing..." + newline);
		m.invoke(null, passedArgs);
		return advArgs;
	}

	private void train(String outputDir, String collectionPath, String[] advArgs)
			throws ClassNotFoundException, NoSuchMethodException,
			IllegalAccessException, InvocationTargetException, IOException {
		// Vectors to topics
		ArrayList<String> trainingParameters = new ArrayList<String>();
		for (int i = 0; i < advArgs.length; i = i + 2) {
			if (fieldOptionMap.containsKey(advArgs[i])) {
				if (fieldOptionMap.get(advArgs[i])[2].equals("train")) {
					trainingParameters.add(advArgs[i]);
					trainingParameters.add(advArgs[i + 1]);
				}
			} else if (checkBoxOptionMap.containsKey(advArgs[i])) {
				if (checkBoxOptionMap.get(advArgs[i])[2].equals("train")) {
					trainingParameters.add(advArgs[i]);
					trainingParameters.add(advArgs[i + 1]);
				}
			} else if (otherOptionsMap.containsKey(advArgs[i])) {
				if (otherOptionsMap.get(advArgs[i]).equals("train")) {
					trainingParameters.add(advArgs[i]);
					trainingParameters.add(advArgs[i + 1]);
				}
			}
		}

		String stateFile = outputDir + File.separator + "output_state.gz";
		String outputDocTopicsFile = outputDir + File.separator
				+ "output_doc_topics.txt";
		String topicKeysFile = outputDir + File.separator + "output_topic_keys";
		Class clazz = Class.forName("cc.mallet.topics.tui.Vectors2Topics");

		String[] temp2 = { "--input", collectionPath, "--num-topics",
				numTopicsField.getText(), "--output-state", stateFile,
				"--output-topic-keys", topicKeysFile, "--output-doc-topics",
				outputDocTopicsFile };
		Collections.addAll(trainingParameters, temp2);
		String[] fullTrainArgs = trainingParameters
				.toArray(new String[trainingParameters.size()]);
		// System.out.println(passedArgs);
		// Now invoke the method.
		Class[] argTypes = new Class[] { fullTrainArgs.getClass(), }; // array
		// is
		// Object!
		Method m = clazz.getMethod("main", argTypes);
		Object[] passedArgs = new Object[] { fullTrainArgs };
		System.out.println(Arrays.toString(fullTrainArgs));
		System.out.println("FINISH!");

		log.append("Training..." + newline);
		m.invoke(null, passedArgs);

		GunZipper g = new GunZipper(new File(stateFile));
		g.unzip(new File(outputDir + File.separator + "output_state"));

		outputCsvFiles(outputDir, true);
		// outputHtmlFiles(outputDir);

		System.out.println("Mallet Output files written in " + outputDir
				+ " ---> " + stateFile + " , " + topicKeysFile + newline);
		System.out.println("Csv Output files written in " + outputDir
				+ File.separator + "output_csv");
		System.out.println("Html Output files written in " + outputDir
				+ File.separator + "output_html");
	}

	/**
	 * Output csv files.
	 * 
	 * @param outputDir
	 *            the output directory
	 * @param htmlOutputFlag
	 *            print html output or not
	 */
	private void outputCsvFiles(String outputDir, Boolean htmlOutputFlag) {

		CsvBuilder cb = new CsvBuilder();
		cb.createCsvFiles(Integer.parseInt(numTopicsField.getText()), outputDir);

		if (htmlOutputFlag) {
			HtmlBuilder hb = new HtmlBuilder(cb.getNtd(), new File(
					inputDirTfield.getText()));
			hb.createHtmlFiles(new File(outputDir));
		}

		// Clear extra files
		String[] fileNames = { "topic-input.mallet", "output_topic_keys",
				"output_state.gz", "output_doc_topics.txt", "output_state" };
		delelteFiles(outputDir, fileNames);
	}

	private void delelteFiles(String inputDir, String... fileNames) {
		for (String f : fileNames) {
			if (!(new File(inputDir, f).canWrite())) {
				System.out.println(f);
			}
			Boolean deleted = new File(inputDir, f).delete();
			if (deleted) {
				System.out.println("File " + f + " deleted");
			}

		}
	}

	/**
	 * The main method.
	 * 
	 * @param args
	 *            the arguments
	 */
	public static void main(String[] args) {

		if (args.length > 0) {
			DEFAULT_INPUT_DIR = args[0];
		}
		new TopicModelingTool().go();

	}

}
